`include "c3d_timescale.v"
`include "c3d_defines.v"

module c3d_alu(
    a, b, alu_op, comp_op,
    mult_mac_result,
    result, flag
);

parameter WIDTH = `C3D_OPERAND_WIDTH;

input   [WIDTH-1:0]     a;
input   [WIDTH-1:0]     b;
input   [`C3D_ALUOP_WIDTH-1:0]  alu_op;
input   [`C3D_COMPOP_WIDTH-1:0] comp_op;
input	[WIDTH-1:0]		mult_mac_result;
output  [WIDTH-1:0]     result;
input                   flag;

reg	    [WIDTH-1:0]		result;
reg				        flagforw;
reg				        flagcomp;
reg				        flag_we;
wire	[WIDTH-1:0]		comp_a;
wire	[WIDTH-1:0]		comp_b;
wire				    a_eq_b;
wire				    a_lt_b;
wire	[WIDTH-1:0]		result_sum;
wire	[WIDTH-1:0]		result_and;
wire				    cy_sum;

assign comp_a = {a[WIDTH-1] ^ comp_op[3] , a[WIDTH-2:0]};
assign comp_b = {b[WIDTH-1] ^ comp_op[3] , b[WIDTH-2:0]};
assign a_eq_b = (comp_a == comp_b);
assign a_lt_b = (comp_a < comp_b);
assign {cy_sum, result_sum} = a + b;
assign result_and = a & b;

`ifdef C3D_DEBUG
always @(result) begin
	if (result === 32'bx)
		$display("%t: ALU结果出错!", $time);
end
`endif

// ALU核心
always @(alu_op or a or b or flag or result_sum or result_and or mult_mac_result) begin
	casex (alu_op)
    `C3D_ALUOP_ADD : begin
        result = result_sum;
    end
    `C3D_ALUOP_SUB : begin
        result = a - b;
    end
    `C3D_ALUOP_XOR : begin
        result = a ^ b;
    end
    `C3D_ALUOP_OR  : begin
        result = a | b;
    end
    `C3D_ALUOP_AND : begin
        result = result_and;
    end
    `C3D_ALUOP_IMM : begin
        result = b;
    end
`ifdef C3D_MULT_IMPLEMENTED
    `C3D_ALUOP_MUL : begin
        result = mult_mac_result;
    end
`endif
    `C3D_ALUOP_CMOV: begin
        result = flag ? a : b;
    end
    endcase
end

// flag处理
always @(alu_op or flagcomp) begin
	casex (alu_op)
    `C3D_ALUOP_COMP: begin
        flagforw = flagcomp;
        flag_we = 1'b1;
    end
    default: begin
        flagforw = 1'b0;
        flag_we = 1'b0;
    end
	endcase
end

always @(comp_op or a_eq_b or a_lt_b) begin
	case (comp_op[2:0])
    `C3D_COP_SFEQ:
        flagcomp = a_eq_b;
    `C3D_COP_SFNE:
        flagcomp = ~a_eq_b;
    `C3D_COP_SFGT:
        flagcomp = ~(a_eq_b | a_lt_b);
    `C3D_COP_SFGE:
        flagcomp = ~a_lt_b;
    `C3D_COP_SFLT:
        flagcomp = a_lt_b;
    `C3D_COP_SFLE:
        flagcomp = a_eq_b | a_lt_b;
    default:
        flagcomp = 1'b0;
	endcase
end

endmodule